package com.bfm.core;

import com.bfm.annotation.Component;
import com.bfm.annotation.Controller;
import com.bfm.annotation.Repository;
import com.bfm.annotation.Service;
import com.bfm.util.ClassUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author slm
 * @Description: TODO
 * @Date 2019/12/13
 **/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {

    private boolean isLoadBean = false;

    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

    private final Map<Class<?>, Object> baseMap = new ConcurrentHashMap<>();


    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        HOLDER;

        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }

    }


    public void loadBeans(String basePackage) {
        if (isLoadBean) {
            log.warn("bean 已经加载");
            return;
        }

        Set<Class<?>> packageClass = ClassUtil.getPackageClass(basePackage);
        packageClass.stream()
                .filter(clz -> {
                    for (Class<? extends Annotation> annotaion : BEAN_ANNOTATION) {
                        if (clz.isAnnotationPresent(annotaion)) {
                            return true;
                        }
                    }
                    return false;
                })
                .forEach(clz -> baseMap.put(clz, ClassUtil.newInstance(clz)));
        isLoadBean = true;
    }

    public Object getBean(Class<?> clz) {
        if (null == clz) {
            return null;
        }
        return baseMap.get(clz);
    }

    public Set<Object> getBeans() {
        return new HashSet<>(baseMap.values());
    }

    public Object addBean(Class<?> clz, Object bean) {
        return baseMap.put(clz, bean);
    }

    public void removeBean(Class<?> clz) {
        baseMap.remove(clz);
    }

    public int size() {
        return baseMap.size();
    }

    public Set<Class<?>> getClasses() {
        return baseMap.keySet();
    }

    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
        return baseMap.keySet().stream().filter(clz -> clz.isAnnotationPresent(annotation))
                .collect(Collectors.toSet());
    }

    public Set<Class<?>> getClassesBySuper(Class<?> superClass) {
        return baseMap.keySet().stream().filter(superClass::isAssignableFrom)
                .collect(Collectors.toSet());
    }


}
